package wcg;

import ikelib.byteUtil;

import java.util.Iterator;
import java.util.TreeSet;



// ACT プロトコルのフレームを定義
// (複数データには対応してない)
public class ActProtocolFrame {
	public ActProtocolFrame(){
	}
	// フレームヘッダ
	public static boolean isActHeader(byte[] dat,int sindex){
		if(dat[sindex+0] == 0x31 && dat[sindex+1]==0x41 && dat[sindex+2]==0x59 && dat[sindex+3] == 0x26) return true;
		return false;
	}
	public static void setActHeader(byte[] dat,int sindex){
		dat[sindex+0] = 0x31;
		dat[sindex+1] =0x41;
		dat[sindex+2] =0x59;
		dat[sindex+3] = 0x26;
	}
	// シーケンス番号
	public static void setSeq(byte [] dat,int sindex,long seq){
		dat[sindex+4] = (byte)((seq>>(8*7)) & 0xff);
		dat[sindex+5] = (byte)((seq>>(8*6)) & 0xff);
		dat[sindex+6] = (byte)((seq>>(8*5)) & 0xff);
		dat[sindex+7] = (byte)((seq>>(8*4)) & 0xff);
		dat[sindex+8] = (byte)((seq>>(8*3)) & 0xff);
		dat[sindex+9] = (byte)((seq>>(8*2)) & 0xff);
		dat[sindex+10] = (byte)((seq>>(8*1)) & 0xff);
		dat[sindex+11] = (byte)((seq>>(8*0)) & 0xff);
	}
	public static long getSeq(byte [] data,int sindex){

		long ret = 
			(long)byteUtil.convShort(data[sindex+4])  * 0x1000000000000L +
			(long)byteUtil.convShort(data[sindex+5])  * 0x10000000000L + 
			(long)byteUtil.convShort(data[sindex+6])  * 0x100000000L +
			(long)byteUtil.convShort(data[sindex+7])  * 0x1000000L + 
			(long)byteUtil.convShort(data[sindex+8])  * 0x1000000L +
			(long)byteUtil.convShort(data[sindex+9])  * 0x10000L +
			(long)byteUtil.convShort(data[sindex+10]) * 0x100L +
			(long)byteUtil.convShort(data[sindex+11]) * 0x1L;
		return ret;
	}
	
	// ACK返送単位 （WINDOWサイズ）
	public static void setAckSize(byte [] dat,int sindex,int size){
		dat[sindex+12] = (byte)((size>>(8*1)) & 0xff);
		dat[sindex+13] = (byte)((size>>(8*0)) & 0xff);
	}
	public static int getAckSize(byte [] data,int sindex){
		int ret = 
			byteUtil.convShort(data[sindex+12]) * 0x100 +
			byteUtil.convShort(data[sindex+13]) * 0x1;
		return ret;
	}
	// データタイプ 　　0x00a0 win形式　0x0006 ACK 0x0003 コマンド
	public static int getDataType(byte[] dat,int sindex){
		return byteUtil.convShort(dat[sindex+14])*0x100 + byteUtil.convShort(dat[sindex+15]);
	}
	
	public static boolean isActAck(byte[] dat,int sindex){
		if(getDataType(dat,sindex)==0x0006) return true;
		return false;
	}
	public static void setDataType(byte [] dat,int sindex,int type){
		dat[sindex+14] = (byte)((type>>(8*1)) & 0xff);
		dat[sindex+15] = (byte)((type>>(8*0)) & 0xff);
	}
	// データ長　
	public static int getDataLength(byte[] dat,int sindex){
		return byteUtil.convShort(dat[sindex+16])*0x100 + byteUtil.convShort(dat[sindex+17]);
	}
	public static void setDataLength(byte[] dat,int sindex,int len){
		dat[sindex+16] = (byte)((len>>(8*1)) & 0xff);
		dat[sindex+17] = (byte)((len>>(8*0)) & 0xff);
	}
	// 
	// データ自体は、WINA0のUDPパケット全体！をくるんでいる
	//
	// ACKを作成するために
	public static void setAckBase(byte [] dat,int sindex,long seq){
		dat[sindex+18] = (byte)((seq>>(8*7)) & 0xff);
		dat[sindex+19] = (byte)((seq>>(8*6)) & 0xff);
		dat[sindex+20] = (byte)((seq>>(8*5)) & 0xff);
		dat[sindex+21] = (byte)((seq>>(8*4)) & 0xff);
		dat[sindex+22] = (byte)((seq>>(8*3)) & 0xff);
		dat[sindex+23] = (byte)((seq>>(8*2)) & 0xff);
		dat[sindex+24] = (byte)((seq>>(8*1)) & 0xff);
		dat[sindex+25] = (byte)((seq>>(8*0)) & 0xff);
	}
	// ACKを作成するために
	public static void setAckBitmap(byte [] dat,int sindex,long bm){
		dat[sindex+26] = (byte)((bm>>(8*3)) & 0xff);
		dat[sindex+27] = (byte)((bm>>(8*2)) & 0xff);
		dat[sindex+28] = (byte)((bm>>(8*1)) & 0xff);
		dat[sindex+29] = (byte)((bm>>(8*0)) & 0xff);
	}
	// ackのSETから　ACKBaseとACKBitmapをセットする
	public static void setAckSizeAndBitmap(byte[] dat,int sindex,TreeSet<Long> ack){
		long ackbase = ack.first();
		setAckBase(dat,sindex,ackbase);
		long bitmap=0;
		Iterator<Long> ite = ack.iterator();
		while(ite.hasNext()){
			long a = ite.next();
			bitmap |= 1<<(a-ackbase);
		}
		//System.out.println(String.format("Ack %d,%04x",ackbase,bitmap));
		setAckBitmap(dat,sindex,bitmap);
	}
	
	public static long getAckBase(byte[] dat,int sindex){
		long ret = 
			(long)byteUtil.convShort(dat[sindex+18])*0x100000000000000L+
			(long)byteUtil.convShort(dat[sindex+19])*0x1000000000000L+
			(long)byteUtil.convShort(dat[sindex+20])*0x10000000000L+
			(long)byteUtil.convShort(dat[sindex+21])*0x100000000L+
			(long)byteUtil.convShort(dat[sindex+22])*0x1000000L+
			(long)byteUtil.convShort(dat[sindex+23])*0x10000L+
			(long)byteUtil.convShort(dat[sindex+24])*0x100L+
			(long)byteUtil.convShort(dat[sindex+25]);
		return ret;
	}
	public static long getAckBitmap(byte[] dat,int sindex){
		long ret = 
			(long)byteUtil.convShort(dat[sindex+26])*0x1000000L+
			(long)byteUtil.convShort(dat[sindex+27])*0x10000L+
			(long)byteUtil.convShort(dat[sindex+28])*0x100L+
			(long)byteUtil.convShort(dat[sindex+29]);
		return ret;
	}
	
	public static TreeSet<Integer> getAckBitmapNumbers(byte[] dat,int sindex){
		TreeSet<Integer> ts = new TreeSet<Integer>();
		long map = getAckBitmap(dat,sindex);
		// bitmapはLSB詰め
		int count=0;
		for(int i=0;i<32;i++){
			if((map & (1L<<(i))) !=0L){
				ts.add(i);
				count++;
			}
		}
		System.out.println(String.format("bitmap=%04x count=%d",map,count));
		return ts;
	}	
	// 
	public static TreeSet<Long> getAckNumbers(byte[] dat,int sindex){
		
		TreeSet<Integer> ts = getAckBitmapNumbers(dat,sindex);
		long base = getAckBase(dat,sindex);
		System.out.println(String.format("Base=%d ",base));
		TreeSet<Long> tsret = new TreeSet<Long>();
		Iterator<Integer> ite = ts.iterator();
		while(ite.hasNext()){
			int bb = ite.next();
			tsret.add((long)bb+base);
		}
		return tsret;
	}	
	
	// CRCフィールドにアクセス。（CRC自体は別に計算しておくこと）
	public static int getDataCRC(byte[] dat,int sindex){
		int datalen = sindex + getDataLength(dat,sindex);
		return byteUtil.convShort(dat[datalen+18])*0x100 + byteUtil.convShort(dat[datalen+19]);
	}
	public static void setDataCRC(byte [] dat,int sindex,int crc){
		int datalen = sindex + getDataLength(dat,sindex);
		dat[datalen+18] = (byte)((crc>>(8*1)) & 0xff);
		dat[datalen+19] = (byte)((crc>>(8*0)) & 0xff);
	}
	public static byte[] convWinA0packet(byte[] dat,int sindex){
		int len = getDataLength(dat,sindex);
		byte[] buf = new byte[len];
		System.arraycopy(dat, 18, buf, 0, buf.length);
		return buf;
	}
	
}
